package com.hydra.framework.event.kvo;

import android.util.Log;

import com.hydra.framework.event.core.EventAction;
import com.hydra.framework.event.core.EventReceiver;
import com.hydra.framework.event.core.helper.DefaultEventThreadWrapper;
import com.hydra.framework.event.kvo.helper.KvoHelper.KvoMethodNode;

import java.lang.reflect.Method;
import java.util.ArrayList;

import static com.hydra.framework.event.kvo.helper.KvoHelper.getKvoMethods;

/**
 * Created by Hydra.
 * <p>
 * 1、支持receiver在父类中绑定和通知，但尽量不要有函数覆盖哦
 * 2、支持KvoSource多层继承中父类的绑定和通知，但是注意父类和子类中可能会出现相同的key，
 * 有相同的key并且在子类绑定时，会只使用子类的
 */
public class Kvo {

    public static final String KVO_LOG_TAG = "KvoEvent";

    public static void autoBindingTo(KvoSource source, Object dst) {
        ArrayList<KvoMethodNode> nodes = getKvoMethods(source, dst);
        for (KvoMethodNode node : nodes) {
            source.addBinding(new EventAction(node.methodAnnotation.name()),
                    buildKvoEventReceiver(node.methodAnnotation,
                            dst, node.method));
        }
    }

    public static void autoUnbindingFrom(KvoSource source, Object dst) {
        ArrayList<KvoMethodNode> nodes = getKvoMethods(source, dst);
        for (KvoMethodNode node : nodes) {
            source.removeBinding(new EventAction(node.methodAnnotation.name()),
                    buildKvoEventReceiver(node.methodAnnotation,
                            dst, node.method));
        }
    }

    public static void addKvoBinding(KvoSource source, Object target, String methodName) {
        try {
            Method method = target.getClass().getMethod(methodName, KvoEventIntent.class);

            addKvoBinding(source, target, method);
        } catch (NoSuchMethodException e) {
            Log.e(KVO_LOG_TAG, "add kvo binding failed :" + e.toString());
        }
    }

    public static void addKvoBinding(KvoSource source, Object target, Method method) {
        KvoMethodAnnotation methodAnnotation = method.getAnnotation(KvoMethodAnnotation.class);

        if (methodAnnotation != null) {
            source.addBinding(new EventAction(methodAnnotation.name()), buildKvoEventReceiver(methodAnnotation,
                    target, method));
        }
    }

    public static void removeKvoBinding(KvoSource source, Object target, String methodName) {
        try {
            Method method = target.getClass().getMethod(methodName, KvoEventIntent.class);

            removeKvoBinding(source, target, method);
        } catch (NoSuchMethodException e) {
            Log.e(KVO_LOG_TAG, "remove kvo binding failed :" + e.toString());
        }
    }

    public static void removeKvoBinding(KvoSource source, Object target, Method method) {
        KvoMethodAnnotation methodAnnotation = method.getAnnotation(KvoMethodAnnotation.class);

        if (methodAnnotation != null) {
            source.removeBinding(new EventAction(methodAnnotation.name()), buildKvoEventReceiver(methodAnnotation,
                    target, method));
        }
    }

    private static EventReceiver buildKvoEventReceiver(KvoMethodAnnotation annotation,
                                                       Object target, Method method) {
        return new EventReceiver(target, method, DefaultEventThreadWrapper.thread(annotation.thread()),
                annotation.priority(), annotation.flag());
    }
}
